/* machine_dac.c */

#include <stdio.h>

#include "machine_dac.h"
#include "machine_pin.h"
#include "hal_dac.h"
#include "hal_gpio.h"
#include "hal_rcc.h"

/*
 * Declerations.
 */

/* Local functions. */
STATIC mp_obj_t machine_dac_obj_init_helper(const machine_dac_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

/* Class method, which would be called by class name. */
STATIC     void machine_dac_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind);
STATIC mp_obj_t machine_dac_obj_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
       mp_obj_t machine_dac_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);


/* Instance methods, which would be called by class instance name. */
STATIC mp_obj_t machine_dac_write_u16(mp_obj_t self_in, mp_obj_t value_in);
STATIC mp_obj_t machine_dac_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args);

/*
 * Functions.
 */

/* dac.write_u16(). */
STATIC mp_obj_t machine_dac_write_u16(mp_obj_t self_in, mp_obj_t value_in)
{
    /* self_in is machine_pin_obj_t. */
    machine_dac_obj_t * self = (machine_dac_obj_t *)self_in;

    self->conf->value = mp_obj_get_int(value_in);
    DAC_PutData(self->dac_port, self->dac_channel, self->conf->value,
                (self->conf->align == DAC_ALIGN_RIGHT) ? DAC_Align_12b_Right : DAC_Align_12b_Left);

    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_dac_write_u16_obj, machine_dac_write_u16);

/* dac.init(). */
STATIC mp_obj_t machine_dac_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
    /* args[0] is machine_dac_obj_t. */
    return machine_dac_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_dac_init_obj, 1, machine_dac_init);

/* parameter list. */
typedef enum
{
    DAC_INIT_ARG_ALIGN = 0,
    DAC_INIT_ARG_VALUE,
} machine_dac_init_arg_t;

STATIC mp_obj_t machine_dac_obj_init_helper (
    const machine_dac_obj_t *self, /* machine_dac_obj_t类型的变量，包含硬件信息 */
    size_t n_args, /* 位置参数数量 */
    const mp_obj_t *pos_args, /* 位置参数清单 */
    mp_map_t *kw_args ) /* 关键字参数清单结构体 */
{
    /* 解析参数 */
    static const mp_arg_t allowed_args[] =
    {
        //[ADC_INIT_ARG_MODE] { MP_QSTR_init , MP_ARG_REQUIRED | MP_ARG_BOOL, {.u_bool = false} },
        [DAC_INIT_ARG_ALIGN] { MP_QSTR_align , MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
        [DAC_INIT_ARG_VALUE] { MP_QSTR_value , MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
    };
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    /* setup pinmux. */
    GPIO_Init_Type gpio_init;
    gpio_init.Speed = GPIO_Speed_50MHz;
    gpio_init.Pins = ( 1u << (self->pin_obj->gpio_pin) );
    gpio_init.PinMode = GPIO_PinMode_In_Analog;
    GPIO_Init(self->pin_obj->gpio_port, &gpio_init);

    /* setup the converter. */
    DAC_Init_Type dac_init;
    dac_init.EnableOutBuf = false;
    dac_init.TrgSource = DAC_TrgSource_None;
    DAC_Init(self->dac_port, self->dac_channel, &dac_init);
    DAC_Enable(self->dac_port, self->dac_channel, true);

    /* keep the data align mode. */
    self->conf->align = args[DAC_INIT_ARG_ALIGN].u_int;
    self->conf->value = args[DAC_INIT_ARG_VALUE].u_int;
    DAC_PutData(self->dac_port, self->dac_channel, self->conf->value,
                (self->conf->align == DAC_ALIGN_RIGHT) ? DAC_Align_12b_Right : DAC_Align_12b_Left);

    return mp_const_none;
}

STATIC     void machine_dac_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind)
{
    /* o is the machine_pin_obj_t. */
    (void)kind;
    const machine_dac_obj_t *self = MP_OBJ_TO_PTR(o);
    mp_printf(print, "DAC(%d):%d, on Pin(%s)", self->dac_channel, self->conf->value, qstr_str(self->pin_obj->name));
}

STATIC mp_obj_t machine_dac_obj_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args)
{
    /* self_in is machine_dac_obj_t. */
    mp_arg_check_num(n_args, n_kw, 0, 1, false);
    machine_dac_obj_t *self = self_in;

    if ( n_args == 0 )  /* read value. */
    {
        return MP_OBJ_NEW_SMALL_INT(self->conf->value);
    }
    else /* write value. */
    {
        return machine_dac_write_u16(self_in, args[0]);
    }
}

/* return an instance of machine_dac_obj_t. */
mp_obj_t machine_dac_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    const machine_dac_obj_t *dac = dac_find(args[0]);

    if ( (n_args >= 1) || (n_kw >= 0) )
    {
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来，单独封装成kw_args。 */
        machine_dac_obj_init_helper(dac, n_args - 1, args + 1, &kw_args);
    }

    return (mp_obj_t)dac;
}


/* class locals_dict_table. */
STATIC const mp_rom_map_elem_t machine_dac_locals_dict_table[] =
{
    /* Class instance methods. */
    { MP_ROM_QSTR(MP_QSTR_write_u16  ),  MP_ROM_PTR(&machine_dac_write_u16_obj) },
    { MP_ROM_QSTR(MP_QSTR_init       ),  MP_ROM_PTR(&machine_dac_init_obj     ) },

    /* Class constants. */
    { MP_ROM_QSTR(MP_QSTR_ALIGN_LEFT ),  MP_ROM_INT(DAC_ALIGN_LEFT            ) },
    { MP_ROM_QSTR(MP_QSTR_ALIGN_RIGHT),  MP_ROM_INT(DAC_ALIGN_RIGHT           ) },
};
STATIC MP_DEFINE_CONST_DICT(machine_dac_locals_dict, machine_dac_locals_dict_table);


const mp_obj_type_t machine_dac_type =
{
    { &mp_type_type },
    .name        = MP_QSTR_DAC,
    .print       = machine_dac_obj_print, /* __repr__(), which would be called by print(<ClassName>). */
    .call        = machine_dac_obj_call,  /* __call__(), which can be called as <ClassName>(). */
    .make_new    = machine_dac_obj_make_new, /* create new class instance. */
    .locals_dict = (mp_obj_dict_t *)&machine_dac_locals_dict,
};

/*
 * User functions.
 */
/* 通过本函数都返回一个期望的DAC对象实例。 */
const machine_dac_obj_t *dac_find(mp_obj_t user_obj)
{
    /* 如果传入参数本身就是一个DAC的实例，则直接送出这个DAC。 */
    if ( mp_obj_is_type(user_obj, &machine_dac_type) )
    {
        return user_obj;
    }

    /* 如果传入参数是一个DAC通道号，则通过索引在DAC清单中找到这个通道，然后送出这个通道。 */
    if ( mp_obj_is_small_int(user_obj) )
    {
        uint8_t dac_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);
        if ( dac_idx < machine_dac_num )
        {
            return machine_dac_objs[dac_idx];
        }
    }

    /* 如果传入参数本身就是一个Pin的实例，则通过倒排查询找到包含这个Pin对象的DAC通道。 */
    if ( mp_obj_is_type(user_obj, &machine_pin_type) )
    {
        machine_pin_obj_t * pin_obj = (machine_pin_obj_t *)(user_obj);
        for (uint32_t i = 0u; i < machine_dac_num; i++)
        {
            if (   (pin_obj->gpio_port == machine_dac_objs[i]->pin_obj->gpio_port)
                && (pin_obj->gpio_pin  == machine_dac_objs[i]->pin_obj->gpio_pin)  )
            {
                return machine_dac_objs[i];
            }
        }
    }

    /* 如果传入参数是一个字符串，则通过这个字符串在Pin清单中匹配引脚名字，拿着找到的pin去匹配预定义DAC对象实例绑定的引脚。 */
    const machine_pin_obj_t *pin_obj = pin_find_by_name(&machine_pin_board_pins_locals_dict, user_obj);
    if ( pin_obj )
    {
        for (uint32_t i = 0u; i < machine_dac_num; i++)
        {
            if (   (pin_obj->gpio_port == machine_dac_objs[i]->pin_obj->gpio_port)
                && (pin_obj->gpio_pin  == machine_dac_objs[i]->pin_obj->gpio_pin)  )
            {
                return machine_dac_objs[i];
            }
        }
    }

    mp_raise_ValueError(MP_ERROR_TEXT("DAC doesn't exist"));
}

/* EOF. */

